package scalalang.reflection

import scala.language.experimental.macros
import scala.reflect.macros.Context
import scala.collection.mutable.ListBuffer
import scala.collection.mutable.Stack
import scala.reflect.ClassTag

case class Location(filename: String, line: Int, column: Int)
object Macros {
  def currentLocation: Location = macro impl
  def impl(c: Context): c.Expr[Location] = {
    import c.universe._
    val pos = c.macroApplication.pos
    val clsLocation = typeOf[Location.type].termSymbol // c.mirror..staticModule("Location") // get symbol of "Location" object typeOf[Location.type].termSymbol (or typeOf[Location].typeSymbol if we needed a ClassSymbol
    c.Expr(Apply(Ident(clsLocation), List(Literal(Constant(pos.source.path)), Literal(Constant(pos.line)), Literal(Constant(pos.column)))))
  }

}
object Macros1 {
  def test = macro testimpl
  def testimpl(c: Context): c.Expr[Unit] = c.Expr(c.parse("println(2)"))
}

//class Queryable[T] {
// def map[U](p: T => U): Queryable[U] = macro QImpl.map[T, U]
//}
//object QImpl {
// def map[T: c.WeakTypeTag, U: c.WeakTypeTag]
//        (c: Context)
//        (p: c.Expr[T => U]): c.Expr[Queryable[U]] = ???
//}

object Print {
  def printf(format: String, params: Any*): Unit = macro printf_impl
  def printf_impl(c: Context)(format: c.Expr[String], params: c.Expr[Any]*): c.Expr[Unit] = {
    import c.universe._
    val Literal(Constant(s_format: String)) = format.tree
    val evals = ListBuffer[ValDef]()
    def precompute(value: Tree, tpe: Type): Ident = {
      val freshName = newTermName(c.fresh("eval$"))
      evals += ValDef(Modifiers(), freshName, TypeTree(tpe), value)
      Ident(freshName)
    }
    val paramsStack = Stack[Tree]((params map (_.tree)): _*)
    val refs = s_format.split("(?<=%[\\w%])|(?=%[\\w%])") map {
      case "%d" => precompute(paramsStack.pop, typeOf[Int])
      case "%s" => precompute(paramsStack.pop, typeOf[String])
      case "%%" => Literal(Constant("%"))
      case part => Literal(Constant(part))
    }
    val stats = evals ++ refs.map(ref => reify(print(c.Expr[Any](ref).splice)).tree)
    c.Expr[Unit](Block(stats.toList, Literal(Constant(()))))
  }

}

object TypeWork {
  type HasA = {
    def a
  }
  def t = macro impl
  def impl(c: Context) = {
    import c.universe._
    //    val t=
    reify {
      new {
        def a = println("")
      }
      //TTT.apply _

    }
    //    c.Expr()
  }

}
object Bigger {
  class Helper(val c: Context) {
    def generate: c.Tree = ???
  }
  trait Heler2 {
    val cc: Context
    def generate: cc.Tree = ???
  }
  class Helper3[C <: Context](val c: C) { //用泛型解决
    def generate: c.Tree = ???
  }

  def impl(c: Context): c.Expr[Unit] = {
    val helper = new Helper(c)
    /*Description	Resource	Path	Location	Type
type mismatch;  found   : helper.c.Tree     (which expands to)  helper.c.universe.Tree  required: c.Tree     (which expands to)  c.universe.Tree	Macros.scala	/ScalaDance/src/main/scala/scalalang/reflection	line 62	Scala Problem
       * 
      c.Expr(helper.generate)*/
    val helper2 = new Heler2 { val cc: c.type = c }
    c.Expr(helper2.generate)
  }
}
object LineCollecter {
  def collect[T](collect: PartialFunction[Any, T])(block: Unit): Seq[T] = macro collectImpl[T]

  def collectImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(collect: c.Expr[PartialFunction[Any, T]])(block: c.Expr[Unit]): c.Expr[Seq[T]] = {
    import c.universe._
    
    def wrap(tree:Tree)=Apply(Select(Ident(newTermName("BlockChildrenValCollect")), newTermName("apply")), tree::Nil) //reify { println(c.Expr[Any](tree).splice) }.tree
   

    def lineChange(line:Tree):List[c.universe.Tree]=line match{
       case line:Apply=>wrap(line)::Nil
      case line: Select => wrap(line)::Nil
      case line: Literal => wrap(line)::Nil
      case line @ ValDef(modifiers,termName,typeTree,value)=>ValDef(modifiers,termName,typeTree,lineChange(value).head)::Nil
      //case line @ ModuleDef(modifiers,termName,tpls)=>line::wrap(Ident(termName))::Nil
      case  Block(stats,expr)=>Block(stats.flatMap {line=>lineChange(line)},lineChange(expr).head)::Nil
      case line=>line::Nil
      
    }
    val newBlock=c.Expr[Unit](lineChange(block.tree).head)
    val tree2 = reify {
      object BlockChildrenValCollect {
        var result = scala.collection.mutable.Buffer[T]()
        def apply[T](t: T) = {
          Option(t).collect(collect.splice).foreach { t => result += t }
          t
        }
      }
      
     newBlock.splice
      
      BlockChildrenValCollect.result
    }.tree
    c.Expr[Seq[T]](tree2)
  }

}

